﻿// --------------------------------------------------------------------------------------------------------------------
// <summary>
//   XPathFileValidationStep.cs 
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace TRlib.BizUnit.CustomSteps
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Xml;
    using global::BizUnit;

    /// <summary>
    /// The XPathFileValidationStep reads a FILE from a given locaton and validates the contents.
    /// </summary>
    /// 
    /// <remarks>
    /// The following shows an example of the Xml representation of this test step.
    /// 
    /// <code escaped="true">
    ///    <TestStep assemblyPath="" typeName="XPathFileValidationStep">
    ///        <Timeout>2000</Timeout>
    ///        <Directory>C:\Recv2\</Directory>
    ///        <SearchPattern>TransactionId_*.xml</SearchPattern>
    ///        <DeleteFile>true</DeleteFile>
    ///    
    ///        <XPathList>
    ///            <XPathValidation query="/*[local-name()='PurchaseOrder' and namespace-uri()='http://SendMail.PurchaseOrder']/*[local-name()='PONumber' and namespace-uri()='']">PONumber_0</XPathValidation>
    ///            <XPathValidation query="/*[local-name()='PurchaseOrder' and namespace-uri()='http://SendMail.PurchaseOrder']/*[local-name()='OtherField' and namespace-uri()='']" takeFromCtx="expectedValue" />
    ///        </XPathList>
    ///                    
    ///    </TestStep>
    ///    </code>
    ///    
    ///    <list type="table">
    ///        <listheader>
    ///            <term>Tag</term>
    ///            <description>Description</description>
    ///        </listheader>
    ///        <item>
    ///            <term>Timeout</term>
    ///            <description>Timeout to wait for the FILE to be written, in milisecs</description>
    ///        </item>
    ///        <item>
    ///            <term>Directory</term>
    ///            <description>The directory where the FILE is located</description>
    ///        </item>
    ///        <item>
    ///            <term>SearchPattern</term>
    ///            <description>The search pattern, such as "*.txt"</description>
    ///        </item>
    ///        <item>
    ///            <term>DeleteFile</term>
    ///            <description>true if the file should be deleted, false if it should not</description>
    ///        </item>
    ///        <item>
    ///            <term>XPathList</term>
    ///            <description>Container for one or more &lt;XPathValidation&gt; tags</description>
    ///        </item>
    ///        <item>
    ///            <term>XPathValidation</term>
    ///            <description>Specifies a query and expected result for the query. The result is optional if using @takeFromCtx (see below)</description>
    ///        </item>
    ///        <item>
    ///            <term>XPathValidation/@query</term>
    ///            <description>Required. An xpath expresion</description>
    ///        </item>
    ///        <item>
    ///            <term>XPathValidation/@takeFromCtx</term>
    ///            <description>Optional. Allows you to specify the expected result from the context by supplying a context key</description>
    ///        </item>
    ///    </list>
    /// </remarks>
    public class XPathFileValidationStep : Validator, ITestStep
    {
        #region ITestStep Members
        public void Execute(XmlNode testConfig, Context context)
        {
            // Read config
            var directory = context.ReadConfigAsString(testConfig, "Directory");
            var searchPattern = context.ReadConfigAsString(testConfig, "SearchPattern");
            var deleteFile = context.ReadConfigAsBool(testConfig, "DeleteFile");
            var timeout = context.ReadConfigAsDouble(testConfig, "Timeout");

            // The following is taken directly from the BizUnit FileValidationStep, hence no comments.
            MemoryStream data = null;

            try
            {
                context.LogInfo("Searching for files in: \"{0}{1}\"", directory, searchPattern);

                DateTime endTime = DateTime.Now + TimeSpan.FromMilliseconds(timeout);
                FileInfo[] files;

                do
                {
                    DirectoryInfo di = new DirectoryInfo(directory);
                    files = di.GetFiles(searchPattern);
                    Thread.Sleep(100);
                } 
                while ((files.Length == 0) && (endTime > DateTime.Now));

                if (files.Length == 0)
                {
                    throw new CustomBizUnitExecutionException(string.Format("No files were found at: {0}{1}", directory, searchPattern));
                }

                context.LogInfo("{0} fies were found at : \"{1}{2}\"", files.Length, directory, searchPattern);

                IOException ex = null;
                do
                {
                    try
                    {
                        using (FileStream fs = new FileStream(files[0].FullName, FileMode.Open, FileAccess.Read))
                        {
                            data = StreamHelper.LoadMemoryStream(fs);
                        }
                    }
                    catch (IOException ioex)
                    {
                        context.LogWarning("IOException caught trying to load file, will re-try if within timeout");
                        ex = ioex;
                        Thread.Sleep(100);
                    }
                } 
                while ((null == data) && (endTime > DateTime.Now));

                if (null != ex)
                {
                    throw ex;
                }

                context.LogData(string.Format("Loaded FILE: {0}", files[0].FullName), data);

                XmlNodeList xpathChecks = testConfig.SelectNodes("//XPathList/*");
                if (xpathChecks.Count > 0)
                {
                    data.Seek(0, SeekOrigin.Begin);
                    this.Validate(data, xpathChecks, context);
                }
                else
                {
                    throw new CustomBizUnitExecutionException("No XPathValidation nodes were found in the config");
                }

                if (deleteFile)
                {
                    File.Delete(files[0].FullName);
                }
            }
            finally
            {
                if (null != data)
                {
                    data.Close();
                }
            }

            // end: Taken from BizUnit FileValidationStep
        }
        #endregion
    }
}
